/**
 The MIT License

Copyright (c) <2011> <JPACHUBE Team>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
 */
package com.pachube.cli;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Properties;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.pachube.api.APIVersion;
import com.pachube.api.Data;
import com.pachube.api.Domain;
import com.pachube.api.Exposure;
import com.pachube.api.Feed;
import com.pachube.api.Location;
import com.pachube.api.Pachube;
import com.pachube.commons.impl.DataImpl;
import com.pachube.commons.impl.FeedImpl;
import com.pachube.commons.impl.LocationImpl;
import com.pachube.exceptions.PachubeException;
import com.pachube.factory.PachubeFactory;
import com.pachube.http.client.HttpClient;
import com.pachube.http.client.HttpMethod;
import com.pachube.http.client.HttpRequest;
import com.pachube.http.client.HttpResponse;

/**
 * The Class Main.
 */
public class PachubeCLI {

    private Pachube pachube;

    /**
     * The main method.
     * 
     * @param argv the arguments
     */
    public static void main(String argv[]) {
        try {
            String key;
            String version = null;
            APIVersion apiVersion;
            int argpt = 0;
            if ((argv.length > 2) && argv[0].equals("-k")) {
                key = argv[1];
                if ((argv.length > 4) && argv[2].equalsIgnoreCase("-v")) {
                    version = argv[3];
                    argpt = 4;
                } else {
                    argpt = 2;
                }
            } else {
                Properties properties = new Properties();
                try {
                    properties.load(new FileInputStream("pachube.properties"));
                } catch (IOException e) {
                    System.err.println("can't load pachube.properties");
                    return;
                }
                key = properties.getProperty("key");
                version = properties.getProperty("version");
            }

            if ((version != null) && version.equals("2")) {
                apiVersion = APIVersion.V2;
            } else {
                apiVersion = APIVersion.V1;
            }

            System.out.println("Pachube key is " + key);
            System.out.println("Pachube version is " + key);
            // Creates a Pachube object authenticated using the provided API
            // KEY
            PachubeCLI pachubeCLI = new PachubeCLI(key, apiVersion);

            if (argv.length < argpt) {
                pachubeCLI.printUsage(System.out);
                return;
            }

            String option = argv[argpt];
            if (option.equals("create")) {
                pachubeCLI.create();

            } else if (option.equals("update")) {
                if (argv.length != argpt + 2) {
                    pachubeCLI.printUsage(System.out);
                    return;
                }

                pachubeCLI.update(Integer.parseInt(argv[argpt + 1]));

            } else if (option.equals("feeds")) {
                if (argv.length != argpt + 2) {
                    pachubeCLI.printUsage(System.out);
                    return;
                }
                pachubeCLI.feeds(argv[argpt + 1]);

            } else {
                pachubeCLI.printUsage(System.out);
                return;
            }

        } catch (PachubeException e) {
            // If an exception occurs it will print the error message from the
            // failed HTTP command
            System.err.println(e.errorMessage);
        } catch (IOException e) {
            System.err.println(e);
        } catch (JSONException e) {
            System.err.println(e);
        }
    }

    public PachubeCLI(String key, APIVersion apiVersion) {
        PachubeFactory pachubeFactory = new PachubeFactory();
        this.pachube = pachubeFactory.createPachube(key, apiVersion);
    }

    /**
     * Feeds.
     * 
     * @param username the username
     * @throws PachubeException the pachube exception
     * @throws JSONException the jSON exception
     * @link http://json-lib.sourceforge.net/index.html
     */
    public void feeds(String username) throws PachubeException, JSONException {

        HttpClient client = new HttpClient("www.pachube.com");

        HttpRequest hr = new HttpRequest("http://www.pachube.com/users/"
                + username + "/feeds.json");
        hr.setMethod(HttpMethod.GET);
        HttpResponse g = client.send(hr);

        if (g.getHeaderItem("Status").equals("HTTP/1.1 200 OK")) {
            // System.out.println(g.getBody());
            JSONObject jsonObject = new JSONObject(g.getBody() + "}"); // add a
                                                                       // bracket
                                                                       // since
                                                                       // it is
                                                                       // missing
                                                                       // in the
                                                                       // returned
                                                                       // JSON
            // System.out.println(jsonObject.toString(2));

            JSONArray results = jsonObject.getJSONArray("results");
            int len = results.length();
            System.out.print(len);
            System.out.print(" feeds :");
            for (int i = 0; i < len; i++) {
                if (i != 0) {
                    System.out.print(", ");
                }
                JSONObject f = results.getJSONObject(i);
                System.out.print(f.getInt("id") + " (" + f.getString("title")
                        + ")");
            }
            System.out.println();

        } else {
            throw new PachubeException(g.getHeaderItem("Status"));
        }
    }

    /**
     * Input data.
     * 
     * @param datas the datas
     * @param in the in
     * @param out the out
     * @param flagOptTag the flag opt tag
     * @return the data impl
     * @throws NumberFormatException the number format exception
     * @throws IOException Signals that an I/O exception has occurred.
     * @throws PachubeException the pachube exception
     */
    private Data inputData(ArrayList<Data> datas, BufferedReader in,
            PrintStream out, boolean flagOptTag) throws NumberFormatException,
            IOException, PachubeException {
        String line = null;

        out.println("Data ident:");
        do {
            line = in.readLine();
        } while (line.trim().length() == 0);
        int id = Integer.parseInt(line.trim());

        Data data = null;
        if (datas != null) {
            for (Data d : datas) {
                if (d.getId() == id) {
                    data = d;
                    break;
                }
            }
        }
        if (data == null) {
            data = new DataImpl();
        }
        data.setId(id);

        if (flagOptTag) {
            out.println("Data tag (opt):");
            if ((line = in.readLine()).length() != 0) {
                data.setTag(line.trim());
            }

        } else {
            out.println("Data tag:");
            do {
                line = in.readLine();
            } while (line.trim().length() == 0);
            data.setTag(line.trim());
        }
        out.println("Data value:");
        do {
            line = in.readLine();
        } while (line.trim().length() == 0);
        data.setValue(line.trim());

        out.println("Data min value (opt):");
        if ((line = in.readLine()).length() != 0) {
            data.setMinValue(Double.parseDouble(line.trim()));
        }

        out.println("Data max value (opt):");
        if ((line = in.readLine()).length() != 0) {
            data.setMaxValue(Double.parseDouble(line.trim()));
        }

        return data;
    }

    /**
     * Input location.
     * 
     * @param location the location
     * @param in the in
     * @param out the out
     * @return the location
     * @throws NumberFormatException the number format exception
     * @throws IOException Signals that an I/O exception has occurred.
     * @throws PachubeException the pachube exception
     */
    private Location inputLocation(Location location, BufferedReader in,
            PrintStream out) throws NumberFormatException, IOException,
            PachubeException {
        String line = null;

        out.println("Location name (opt):");
        if ((line = in.readLine()).length() != 0) {
            if (location == null) {
                location = new LocationImpl();
            }

            location.setName(line.trim());

            out.println("Location lattitude:");
            do {
                line = in.readLine();
            } while (line.trim().length() == 0);
            location.setLat(Float.parseFloat(line.trim()));

            out.println("Location longitude:");
            do {
                line = in.readLine();
            } while (line.trim().length() == 0);
            location.setLon(Float.parseFloat(line.trim()));

            out.println("Location elevation:");
            do {
                line = in.readLine();
            } while (line.trim().length() != 0);
            location.setElevation(Float.parseFloat(line.trim()));

            out.println("Location domain (opt):");
            if ((line = in.readLine()).length() != 0) {
                line = line.trim();
                if (line.equals("physical")) {
                    location.setDomain(Domain.PHYSICAL);
                } else if (line.equals("virtual")) {
                    location.setDomain(Domain.VIRTUAL);
                }
            } else {
                location.setDomain(Domain.PHYSICAL);
            }

            out.println("Location Exposure (opt):");
            if ((line = in.readLine()).length() != 0) {
                line = line.trim();
                if (line.equals("indoor")) {
                    location.setExposure(Exposure.INDOOR);
                } else if (line.equals("outdoor")) {
                    location.setExposure(Exposure.OUTDOOR);
                }
            } else {
                location.setExposure(Exposure.OUTDOOR);
            }
            return location;
        }
        return null;

    }

    /**
     * Update.
     * 
     * @param p the p
     * @param feedid the feedid
     * @throws PachubeException the pachube exception
     * @throws IOException Signals that an I/O exception has occurred.
     */
    private void update(int feedid) throws PachubeException, IOException {
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in,
                "UTF-8"));
        PrintStream out = System.out;
        Feed f = this.pachube.getFeed(feedid);

        // System.out.println(f.toXML());

        // String line = null;

        Location location = inputLocation(f.getLocation(), in, out);
        if (location != null) {
            f.setLocation(location);
        }

        System.out.println("XML is :" + f.toXML());

        System.out.println("updating ...");
        f.updateDatastream(inputData(f.getData(), in, out, true));
        System.out.println("updated");

    }

    /**
     * Creates the.
     * 
     * @param p the p
     * @throws IOException Signals that an I/O exception has occurred.
     * @throws PachubeException the pachube exception
     */
    private void create() throws IOException, PachubeException {

        BufferedReader in = new BufferedReader(new InputStreamReader(System.in,
                "UTF-8"));
        PrintStream out = System.out;
        Feed f = new FeedImpl();

        String line = null;

        out.println("Title:");
        do {
            line = in.readLine();
        } while (line.trim().length() != 0);
        f.setTitle(line.trim());

        out.println("Description:");
        do {
            line = in.readLine();
        } while (line.trim().length() != 0);
        f.setDescription(line.trim());

        out.println("Web site (opt):");
        if ((line = in.readLine()).length() != 0) {
            f.setWebsite(new URL(line.trim()));
        }

        Location location = inputLocation(null, in, out);
        if (location != null) {
            f.setLocation(location);
        }

        f.addData(inputData(null, in, out, false));

        System.out.println("XML is :" + f.toXML());

        System.out.println("creating ...");
        Feed g = this.pachube.createFeed(f);
        System.out.println("created");

        // The Feed 'f' is does not represent the feed on pachube any
        // Changes made to this object will not alter the online feed.

        out.println("The id of the new feed is:");
        out.println(g.getId());

    }

    /**
     * Prints the usage.
     * 
     * @param out the out
     */
    private void printUsage(PrintStream out) {
        out.print("Usage:\n"
                + "java -jar jpachubeutil [-k key] [-v apiversion] create\n"
                + "java -jar jpachubeutil [-k key] [-v apiversion] update <feedid>\n"
                + "java -jar jpachubeutil [-k key] [-v apiversion] feeds  <username>\n"

        );
    }
}